<!DOCTYPE html>
<html>

<head>
  <title>QR Code Correction</title>
  <style>
    .container {
      margin: 20px;
      display: flex;
      flex-direction: column;
      gap: 15px;
    }

    .canvas-container {
      display: flex;
      gap: 20px;
    }


    .controls {
      margin-bottom: 15px;
    }

    input[type="file"] {
      padding: 8px;
      background: #f0f0f0;
      border-radius: 4px;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="controls">
      <input type="file" id="imageInput" accept="image/*">
    </div>
    <div class="canvas-container">
      <div>
        <h3>Original Image</h3>
        <canvas id="sourceCanvas"></canvas>
      </div>
      <div>
        <h3>Corrected QR</h3>
        <canvas id="correctedCanvas"></canvas>
      </div>
      <div>
        <h3> QR</h3>
        <canvas id="qrCanvas"></canvas>
      </div>
    </div>
  </div>

  <!-- 引入 jsQR 库 -->
  <script src="../../plugins/qr.min.js"></script>


  <script>
    const imageInput = document.getElementById('imageInput');
    const sourceCanvas = document.getElementById('sourceCanvas');
    const correctedCanvas = document.getElementById('correctedCanvas');
    const qrCanvas = document.getElementById('qrCanvas');

    const sourceCtx = sourceCanvas.getContext('2d');
    const correctedCtx = correctedCanvas.getContext('2d');
    const qrCtx = qrCanvas.getContext('2d');

    let originalImage = null;

    console.log('qr', qr);


    // 处理文件选择
    imageInput.addEventListener('change', function (e) {
      const file = e.target.files[0];
      if (!file) return;

      const reader = new FileReader();
      reader.onload = function (event) {
        originalImage = new Image();
        originalImage.onload = processImage;
        originalImage.src = event.target.result;
      };
      reader.readAsDataURL(file);
    });

    function calculateSharpness (imageData) {
      // 转换为灰度数据
      const grayData = new Uint8Array(imageData.width * imageData.height);
      for (let i = 0, j = 0; i < imageData.data.length; i += 4, j++) {
        grayData[j] = 0.299 * imageData.data[i] + 0.587 * imageData.data[i + 1] + 0.114 * imageData.data[i + 2];
      }

      // 使用现有laplacian函数
      return laplacian(grayData, imageData.width, imageData.height);
    }

    function laplacian (gray, width, height) {
      let sum = 0, sum2 = 0, N = width * height;
      for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
          let L = 0;
          if (x > 0 && x < width - 1 && y > 0 && y < height - 1) {
            L = -4 * gray[y * width + x]
              + gray[y * width + (x - 1)]
              + gray[y * width + (x + 1)]
              + gray[(y - 1) * width + x]
              + gray[(y + 1) * width + x];
          }
          sum += L;
          sum2 += L * L;
        }
      }
      const mean = sum / N;
      return sum2 / N - mean * mean;
    }

    // 处理图片加载
    function processImage () {
      // 设置Canvas尺寸为图片原始尺寸
      sourceCanvas.width = originalImage.naturalWidth;
      sourceCanvas.height = originalImage.naturalHeight;

      correctedCanvas.width = originalImage.naturalWidth;
      correctedCanvas.height = originalImage.naturalHeight;

      sourceCtx.clearRect(0, 0, sourceCanvas.width, sourceCanvas.height);


      sourceCtx.drawImage(originalImage, 0, 0);

      try {
        // 获取二维码位置信息
        const imageData = sourceCtx.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height);

        console.log('imageData', imageData);

        console.time()
        const qrResult = qr.decodeQR(imageData, {
          cropToSquare: true,
          pointsOnDetect: (points) => {
            console.log('pointsOnDetect', points);
          },
        });
        console.timeEnd()

        if (!qrResult) {
          throw new Error('未检测到二维码！');
        }

        console.log('qrResult.location', qrResult);


      } catch (error) {
        console.error('图片处理出错:', error);
        alert(error.message);
      }
    }



    function calculateSkewAngles (location, width, height) {
      const { topLeftCorner, topRightCorner, bottomLeftCorner } = location;
      // 1. 向量
      const dx1 = topRightCorner.x - topLeftCorner.x;
      const dy1 = topRightCorner.y - topLeftCorner.y;
      const dx2 = bottomLeftCorner.x - topLeftCorner.x;
      const dy2 = bottomLeftCorner.y - topLeftCorner.y;
      // 2. 大小
      const L1 = Math.hypot(dx1, dy1);
      const L2 = Math.hypot(dx2, dy2);
      const codeSize = (L1 + L2) / 2;
      // 3. 旋转角 φ（**弧度**）
      const phi = Math.atan2(dy1, dx1);
      const rotationAngle = phi;
      // 4. 构造仿射矩阵 M
      const m00 = dx1 / codeSize, m10 = dy1 / codeSize;
      const m01 = dx2 / codeSize, m11 = dy2 / codeSize;
      // 5. Rᵀ · M
      const cosφ = Math.cos(phi), sinφ = Math.sin(phi);
      const a_p = cosφ * m00 + sinφ * m10;
      const b_p = cosφ * m01 + sinφ * m11;
      const c_p = -sinφ * m00 + cosφ * m10;
      const d_p = -sinφ * m01 + cosφ * m11;
      // 6. 剪切角度 (仍然以度为单位)
      const horizontalSkewAngle = Math.atan2(b_p, a_p) * 180 / Math.PI;
      const verticalSkewAngle = Math.atan2(c_p, d_p) * 180 / Math.PI;
      // 7. 校正 topLeftCorner
      const cx = width / 2, cy = height / 2;
      const cosm = Math.cos(-phi), sinm = Math.sin(-phi);
      const dx = topLeftCorner.x - cx, dy = topLeftCorner.y - cy;
      const corrX = dx * cosm - dy * sinm + cx;
      const corrY = dx * sinm + dy * cosm + cy;
      return {
        rotationAngle,
        horizontalSkewAngle,
        verticalSkewAngle,
        codeSize,
        correctedTopLeft: { x: corrX, y: corrY }
      };
    }


    // 处理二维码矫正
    function processQRCorrection (imageData, location) {
      try {
        const correctionParams = calculateSkewAngles(location, correctedCanvas.width, correctedCanvas.height)

        console.log('correctionParams', imageData, location, correctionParams);

        // correctedCtx.clearRect(0, 0, correctedCanvas.width, correctedCanvas.height);
        correctedCtx.save();
        correctedCtx.translate(correctedCanvas.width / 2, correctedCanvas.height / 2);
        correctedCtx.rotate(-correctionParams.rotationAngle);
        // correctedCtx.putImageData(imageData, -correctedCanvas.width / 2, -correctedCanvas.height / 2, 0, 0, correctedCanvas.width, correctedCanvas.height);
        correctedCtx.drawImage(sourceCanvas, 0, 0, correctedCanvas.width, correctedCanvas.height, -correctedCanvas.width / 2, -correctedCanvas.height / 2, correctedCanvas.width, correctedCanvas.height);
        correctedCtx.restore();

        applyCorrection(correctionParams);
      } catch (error) {
        console.error('矫正失败:', error);
        alert('矫正过程中发生错误');
      }
    }

    // 应用矫正
    function applyCorrection (params) {
      const { codeSize, correctedTopLeft } = params;

      qrCanvas.width = codeSize
      qrCanvas.height = codeSize
      qrCtx.putImageData(imageData, 0, 0,);

      const imageData = correctedCtx.getImageData(correctedTopLeft.x, correctedTopLeft.y, qrCanvas.width, qrCanvas.height)

      console.log('清晰度：', imageData, calculateSharpness(imageData));

      qrCtx.putImageData(imageData, 0, 0,);
    }
  </script>
</body>

</html>